Skip to content

Conversation

elizabetdev
Copy link
Contributor

@elizabetdev elizabetdev commented Sep 30, 2025

Closes HDX-2436 and closes HDX-2534

This PR adds copy-to-clipboard buttons in RawLogTable for log line data and URLs, with improved button styling. It also adds row field buttons.

image

Copy link

changeset-bot bot commented Sep 30, 2025

🦋 Changeset detected

Latest commit: 3d26a38

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@hyperdx/app Minor
@hyperdx/api Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

vercel bot commented Sep 30, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
hyperdx-v2-oss-app Ready Ready Preview Comment Oct 10, 2025 0:12am

Copy link
Contributor

github-actions bot commented Sep 30, 2025

E2E Test Results

All tests passed • 25 passed • 3 skipped • 223s

Status Count
✅ Passed 25
❌ Failed 0
⚠️ Flaky 0
⏭️ Skipped 3

View full report →

@brandon-pereira
Copy link
Member

@elizabetdev the issue isn't with exclude specifically, just string rows in general that are json.

When I click the filter button on following data for example: Screenshot 2025-10-08 at 9 08 59 AM

This is what the sidebar shows:
Screenshot 2025-10-08 at 9 09 10 AM

I do see the same behavior here: so maybe it's indented, thought I'd confirm its intentional / or another ticket/issue
Screenshot 2025-10-08 at 9 16 04 AM

@elizabetdev
Copy link
Contributor Author

Thanks @brandon-pereira! I see the issue now. I had assumed the filters would work just by adding them to any field (even though for something like timeStamp it might not make much sense). I realize now it’s not that straightforward.

For now, I’ve enabled filters only for SeverityText and ServiceName. I can also disable them completely and just keep the copy action for each field if that’s preferred.

Since this PR is mainly about setting up the UI to support field actions, I think it makes sense for an engineer to define the actual logic for showing more actions later on.

@brandon-pereira
Copy link
Member

Thanks @brandon-pereira! I see the issue now. I had assumed the filters would work just by adding them to any field (even though for something like timeStamp it might not make much sense). I realize now it’s not that straightforward.

For now, I’ve enabled filters only for SeverityText and ServiceName. I can also disable them completely and just keep the copy action for each field if that’s preferred.

Since this PR is mainly about setting up the UI to support field actions, I think it makes sense for an engineer to define the actual logic for showing more actions later on.

Perfect, that makes sense. This PR LGTM - based on the above- do we want to re-introduce the exclude filter? I can see that being valuable for some.

brandon-pereira
brandon-pereira previously approved these changes Oct 8, 2025
brandon-pereira
brandon-pereira previously approved these changes Oct 8, 2025
@teeohhem
Copy link
Contributor

teeohhem commented Oct 9, 2025

@claude

Copy link

claude bot commented Oct 9, 2025

Claude finished @teeohhem's task —— View job


Need Clarification

Hi @teeohhem! I see you've mentioned me, but I'm not sure what you'd like me to help with. Here are some things I can do:

  1. Provide a code review - Review the PR changes and provide feedback on code quality, potential issues, etc.
  2. Answer the open question - Provide input on whether to re-introduce the exclude filter (from @brandon-pereira's comment)
  3. Implement the exclude filter - Add back the exclude filter functionality if that's desired
  4. General PR analysis - Summarize the changes and current status
  5. Something else - Just let me know what you need!

Could you please clarify what you'd like me to do?


Job RunBranch: search-results-copy-log-line

MikeShi42
MikeShi42 previously approved these changes Oct 9, 2025
Copy link

claude bot commented Oct 10, 2025

Code Review - PR #1227

Thank you for this feature addition! This PR adds useful copy-to-clipboard functionality and filtering options to the log table. Here's my detailed review:


Strengths

Good Architecture & Component Separation

  • Well-structured component breakdown with DBRowTableFieldWithPopover, DBRowTableRowButtons, and DBRowTableIconButton
  • Proper use of Mantine UI components (Popover, Tooltip) following the project's UI component hierarchy
  • Clean separation of concerns between field-level and row-level actions

UX Improvements

  • Smart hover interaction with 1-second disable after click to prevent accidental popover reopening
  • Visual feedback for copy actions (checkmark icon, 2-second timeout)
  • Selective filter button display (only ServiceName/SeverityText) shows thoughtful feature scoping

Code Quality

  • TypeScript interfaces are well-defined
  • Proper use of React hooks (useCallback, useMemo, useDisclosure)
  • Good use of context (RowSidePanelContext) for accessing filter functionality

🔍 Issues & Concerns

1. Memory Leaks (Critical) 🔴

DBRowTableFieldWithPopover.tsx:76-80 & 22-27

  • Multiple timeout references are created but not cleaned up when component unmounts
  • This will cause memory leaks, especially in tables with many rows

Fix Required:

useEffect(() => {
  return () => {
    if (timeoutRef.current) {
      clearTimeout(timeoutRef.current);
    }
    if (hoverDisableTimeoutRef.current) {
      clearTimeout(hoverDisableTimeoutRef.current);
    }
  };
}, []);

Same issue in DBRowTableRowButtons.tsx - the setTimeout callbacks need cleanup.


2. Error Handling Missing 🟡

DBRowTableFieldWithPopover.tsx:76-80 & DBRowTableRowButtons.tsx:22-40

  • navigator.clipboard.writeText() is an async operation that can fail (permissions, HTTPS requirement, etc.)
  • No error handling if clipboard operations fail

Recommendation:

const copyFieldValue = async () => {
  try {
    const value = typeof cellValue === 'string' ? cellValue : `${cellValue}`;
    await navigator.clipboard.writeText(value);
    setIsCopied(true);
    setTimeout(() => setIsCopied(false), 2000);
  } catch (error) {
    console.error('Failed to copy to clipboard:', error);
    // Consider showing an error toast notification
  }
};

3. Accessibility Issues 🟡

DBRowTableFieldWithPopover.tsx:137-144

  • The clickable span lacks proper keyboard navigation
  • No ARIA attributes for screen readers
  • The popover won't be accessible via keyboard

Recommendations:

<span
  onMouseEnter={handleMouseEnter}
  onMouseLeave={handleMouseLeave}
  onClick={handleClick}
  onKeyDown={(e) => {
    if (e.key === 'Enter' || e.key === ' ') {
      handleClick();
    }
  }}
  tabIndex={0}
  role="button"
  aria-label="Show field actions"
  style={{ cursor: 'pointer' }}
>

4. Type Safety 🟡

DBRowTableFieldWithPopover.tsx:15

cellValue: any;  // ❌ Using 'any' type

Recommendation:

cellValue: unknown;  // Better type safety

Then use type guards in the conversion:

const value = typeof cellValue === 'string' 
  ? cellValue 
  : String(cellValue ?? '');

5. URL Manipulation Security 🟡

DBRowTableRowButtons.tsx:29-36

  • Direct URL manipulation could be exploited if getRowWhere() returns unsanitized data
  • The rowWhere parameter could contain extremely long strings causing URL length issues

Recommendations:

  • Add URL length validation
  • Consider URL encoding for the rowWhere parameter
  • Validate the rowWhere output length before setting

6. Performance Considerations 🟢

DBRowTable.tsx:893-924

  • The popover is rendered for every cell in every row, which could impact performance with large tables
  • Consider virtualizing or lazy-loading popovers for better performance

Minor Issue:

  • Repeated calls to JSON.stringify(row, null, 2) on every copy action (acceptable but could be memoized if rows are large)

7. Hardcoded Values 🟢

DBRowTableFieldWithPopover.tsx:39-43

const canFilter =
  columnName &&
  (columnName === 'ServiceName' || columnName === 'SeverityText') &&
  ...

Recommendation:
Extract to a constant or make configurable:

const FILTERABLE_COLUMNS = ['ServiceName', 'SeverityText'];
const canFilter = columnName && FILTERABLE_COLUMNS.includes(columnName) && ...

8. Code Style (Minor) 🟢

packages/api/tsconfig.json

  • The formatting changes (arrays on single lines) are fine but unrelated to the PR's main purpose
  • Consider separating pure formatting changes into separate commits or PRs

🧪 Testing Coverage

Missing:

  • No unit tests for the new components
  • No integration tests for copy/filter functionality
  • No tests for error scenarios (clipboard failures, missing context, etc.)

Recommended Tests:

  1. Test clipboard copy functionality with mocked navigator.clipboard
  2. Test filter button behavior with different column types
  3. Test hover/click interactions and popover state management
  4. Test timeout cleanup on unmount
  5. Test URL generation with various row data

🔒 Security Assessment

✅ No obvious security vulnerabilities for malicious code execution
⚠️ URL manipulation should be validated (mentioned above)
✅ No sensitive data exposure concerns


📊 Overall Assessment

Code Quality: 7/10

  • Well-structured but has critical memory leak issues
  • Good use of modern React patterns

Feature Implementation: 8/10

  • Good UX decisions
  • Solid feature scope

Production Readiness: 6/10

  • Memory leaks must be fixed before merge
  • Error handling needs improvement
  • Accessibility should be addressed

🎯 Recommendations Before Merge

Must Fix (Blocking):

  1. ✅ Add cleanup for timeouts in useEffect to prevent memory leaks
  2. ✅ Add try/catch error handling for clipboard operations

Should Fix:

  1. Add keyboard accessibility and ARIA attributes
  2. Add URL validation/length checks
  3. Change any to unknown for better type safety
  4. Add unit tests for the new components

Nice to Have:

  1. Extract hardcoded column names to constants
  2. Consider performance optimization for large tables
  3. Add error toast notifications for failed operations

Great work on the feature implementation! The UX improvements are solid and the component architecture follows good React patterns. The main concerns are the memory leaks and error handling which should be addressed before merging.

Let me know if you'd like clarification on any of these points! 🚀

@elizabetdev elizabetdev dismissed stale reviews from MikeShi42 and brandon-pereira via 98b4122 October 10, 2025 10:48
Copy link

claude bot commented Oct 10, 2025

PR Review: Add copy-to-clipboard buttons in RawLogTable

Great work on this feature addition! The implementation is well-structured and follows the codebase conventions. Here's my detailed feedback:


Strengths

  1. Good Component Decomposition: Breaking down the functionality into reusable components (DBRowTableIconButton, DBRowTableFieldWithPopover, DBRowTableRowButtons) is excellent and follows React best practices.

  2. Proper Mantine UI Usage: The code correctly uses Mantine components as specified in CLAUDE.md, including Popover, Tooltip, and UnstyledButton.

  3. Memory Leak Prevention: Excellent timeout cleanup in useEffect (lines 35-44 in DBRowTableFieldWithPopover.tsx).

  4. Good UX Considerations: The hover disable timeout mechanism (1000ms) prevents accidental popover reopening, which is thoughtful for user experience.

  5. Error Handling: Clipboard operations are properly wrapped in try-catch blocks with appropriate error logging.


🐛 Potential Issues

1. Missing Clipboard API Fallback

Severity: Medium
Location: DBRowTableFieldWithPopover.tsx:91, DBRowTableRowButtons.tsx:25, 43

The clipboard API might not be available in all browsers or contexts (e.g., non-HTTPS). Consider adding a fallback for older browsers or insecure contexts.

2. Hardcoded Column Names for Filtering

Severity: Low
Location: DBRowTableFieldWithPopover.tsx:51-55

The filterable columns are hardcoded (ServiceName, SeverityText). Consider making this configurable via props or a constant array for better maintainability.

3. Type Safety Issue

Severity: Low
Location: DBRowTableFieldWithPopover.tsx:15

Using 'any' type for cellValue goes against the codebase guideline. Consider using a union type like: string | number | boolean | null | undefined


Performance Considerations

  1. Timeout State Management: The component creates timeouts on every hover interaction. The cleanup is proper, so this is acceptable for now.

  2. JSON Stringification: For very large row objects, JSON.stringify could be slow, but you're already doing this only on click, which is good.


🔒 Security Considerations

  1. No Critical Issues: The code doesn't introduce obvious security vulnerabilities.
  2. URL Construction: The URL construction in copyRowUrl looks safe as it uses the URL API and searchParams.set.
  3. SQL Injection: The useRowWhere hook uses SqlString.format properly to prevent SQL injection - good!

🧪 Test Coverage

Concern: No test files were added for the new components. Consider adding:

  1. Unit tests for DBRowTableIconButton (click handlers, tooltip rendering)
  2. Unit tests for DBRowTableFieldWithPopover (popover interactions, copy functionality)
  3. Integration tests for the clipboard functionality
  4. Tests for the filter functionality with different column types

📝 Code Quality Suggestions

1. Extract Magic Numbers

Location: DBRowTableFieldWithPopover.tsx:69, 85

Consider extracting timeout values (100ms, 1000ms) to named constants at the top of the file for better maintainability.

2. Inconsistent iconSize Usage

Location: Various files

The icon sizes vary (14 vs 12). Consider standardizing or documenting why they differ.

3. Accessibility Improvements

Current: tabIndex={-1} and aria-hidden="true" on the popover target

Consider:

  • Adding keyboard shortcuts for copy
  • Making the buttons keyboard accessible
  • Adding ARIA labels for screen readers

🎨 Style & Convention Observations

  1. Good: Proper use of SCSS modules and CSS-in-JS via Mantine's styling system
  2. Good: Consistent naming conventions (handleMouseEnter, copyFieldValue, etc.)
  3. Minor: The tsconfig.json formatting change in packages/api/tsconfig.json is unrelated to this PR's purpose (though it's a good cleanup)

🔄 API Changes

Modified: RowSidePanelContextProps.onPropertyAddClick signature now includes an optional action parameter

Impact: This is backward compatible due to the optional parameter. Good design! 👍


🎯 Recommendations

  1. High Priority: Add test coverage for the new components
  2. Medium Priority: Add clipboard API fallback for broader browser support
  3. Low Priority: Extract magic numbers to constants
  4. Low Priority: Make filterable columns configurable
  5. Low Priority: Improve type safety by replacing any types

Overall Assessment

This is a solid feature implementation that follows the codebase conventions well. The code is clean, well-structured, and demonstrates good React patterns. With the addition of tests and minor improvements suggested above, this will be production-ready.

Recommendation: Approve with minor suggestions for improvement.


Great work, @elizabetdev! 🎉

Copy link

claude bot commented Oct 10, 2025

Code Review - PR #1227

Thanks for the contribution! This PR adds useful copy-to-clipboard functionality for log table rows and fields. Overall, the implementation is solid and follows good practices. Here are my findings:

✅ Strengths

  1. Good Component Architecture: New components are well-structured and reusable:

    • DBRowTableFieldWithPopover - Field-level interactions
    • DBRowTableRowButtons - Row-level actions
    • DBRowTableIconButton - Shared button component
  2. Memory Leak Prevention: Proper cleanup of timeouts in DBRowTableFieldWithPopover.tsx:35-44 using useEffect cleanup function.

  3. Mantine UI Adoption: Following project guidelines by using Mantine components (Popover, Tooltip, UnstyledButton) instead of Bootstrap.

  4. Accessibility Considerations: Uses aria-hidden and tabIndex appropriately for interactive elements.

  5. User Experience: Smart hover delay logic (100ms before closing, 1s disable after click) prevents accidental reopens.

  6. Changeset Added: Includes proper changeset documentation for the release.

🐛 Potential Bugs

  1. Logic Error in JSON String Detection (DBRowTableRowButtons.tsx:31-34)

    if (
      (typeof value === 'string' && value.startsWith('{')) ||
      value.startsWith('[')
    ) {

    Issue: The second condition value.startsWith('[') will throw if value is not a string (no type guard).

    Fix:

    if (
      typeof value === 'string' && 
      (value.startsWith('{') || value.startsWith('['))
    ) {
  2. Missing Import (HDXMultiSeriesTableChart.tsx:5)

    import { IconGripVertical } from '@tabler/icons-react';

    Issue: This import is added but never used in the file. Should be removed or the feature should be implemented.

⚠️ Security Concerns

  1. URL State Injection (DBRowTableRowButtons.tsx:64)
    The rowWhere query parameter could become very large and potentially contain malicious SQL-like content. While getRowWhere uses SqlString.format, the URL itself isn't validated.

    Recommendation: Consider URL length limits and validate/sanitize before copying to clipboard. Document max URL length.

  2. XSS Consideration: The clipboard API is used correctly, but ensure that any content displayed from cellValue is properly escaped when rendered in the UI (looks good in current implementation).

🚀 Performance Considerations

  1. Popover on Every Cell (DBRowTableFieldWithPopover.tsx)
    Creating a popover for every table cell could impact performance with large datasets. The popovers are lazily opened, which is good, but each still creates DOM nodes and event listeners.

    Suggestion: Consider performance testing with 500+ rows. If issues arise, you might need:

    • Virtualization-aware implementation
    • Portal-based single popover that repositions
    • Debouncing hover events
  2. Timeout Memory (DBRowTableFieldWithPopover.tsx:94,52)
    The setTimeout for copy feedback (2s) isn't cleared if the component unmounts. While not critical, it's a minor memory leak.

    Fix: Add copyTimeoutRef and clear it in the cleanup function.

  3. JSON Parsing on Every Copy (DBRowTableRowButtons.tsx:29-47)
    The row data is parsed every time the copy button is clicked, even if the user copies multiple times.

    Optimization: Consider memoizing the parsed result with useMemo.

🎨 Code Quality

  1. Magic Numbers: buttonSize = 20, gapSize = 4, delay times (100ms, 1000ms, 2000ms) should be extracted as named constants at the file or module level for maintainability.

  2. Duplicate Code: addFilter and excludeFilter functions in DBRowTableFieldWithPopover.tsx:101-117 have significant duplication.

    Suggestion:

    const handleFilter = useCallback((action: 'include' | 'exclude') => {
      if (canFilter) {
        const value = typeof cellValue === 'string' ? cellValue : String(cellValue ?? '');
        onPropertyAddClick(columnName, value, action);
        handleClick();
      }
    }, [canFilter, cellValue, columnName, onPropertyAddClick]);
  3. Error Handling: Error handling for clipboard operations logs to console but doesn't inform the user. Consider adding toast notifications for better UX.

  4. Type Safety: Record<string, any> is used throughout. Consider defining proper interfaces for row data to improve type safety.

  5. Hard-coded Column Names (DBRowTableFieldWithPopover.tsx:51-53)

    columnName === 'ServiceName' || columnName === 'SeverityText'

    Issue: Hard-coding column names reduces flexibility.

    Suggestion: Make this configurable via props or a config object, or use a column metadata approach.

🧪 Test Coverage

Missing: No test files were added for the new components. Given the complexity of the interaction logic (hover states, timeouts, clipboard operations), tests would be valuable.

Recommendations:

  • Unit tests for DBRowTableFieldWithPopover hover/click behavior
  • Tests for clipboard operations (can mock navigator.clipboard)
  • Tests for the JSON parsing logic in DBRowTableRowButtons
  • Tests for timeout cleanup to prevent memory leaks

📝 Style & Conventions

  1. Consistent with CLAUDE.md: Good adherence to project guidelines:

    • ✅ Uses Mantine UI as primary component library
    • ✅ Functional components with hooks
    • ✅ TypeScript interfaces defined
    • ✅ Files are reasonably sized
  2. Import Ordering: Imports are well-organized (React → external → internal → styles)

  3. Code Formatting: The packages/api/tsconfig.json formatting change is unrelated to the feature but improves consistency.

🔍 Additional Notes

  1. Layout Changes (DBSearchPage.tsx): The PR includes significant layout restructuring (moving "Resume Live Tail" button, changing container heights). These changes seem beneficial but should be highlighted in the PR description as they affect more than just the copy buttons.

  2. CSS Module Additions: New styles in LogTable.module.scss look good. Consider documenting the new classes if they're meant to be reused.

  3. onPropertyAddClick API Change: The signature change adding the action parameter is a good enhancement for the filter functionality.

🎯 Summary

Approval Recommendation: ✅ Approve with minor fixes

The PR delivers valuable functionality with good code structure. The critical issue is the type safety bug in DBRowTableRowButtons.tsx:31-34. Other items are optimization suggestions and code quality improvements.

Must Fix Before Merge:

  1. Fix the type guard logic error in JSON string detection

Should Fix Before Merge:

  1. Remove unused import (IconGripVertical)
  2. Add timeout cleanup for copy feedback state

Nice to Have:

  1. Add test coverage
  2. Extract magic numbers to constants
  3. Reduce code duplication in filter handlers
  4. Add user-facing error notifications

Great work overall! 🎉

// Parse JSON string fields to make them proper JSON objects
const parsedRow = Object.entries(cleanRow).reduce(
(acc, [key, value]) => {
if (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this code in 3 places already, can we make another ticket to refactor this to be a utility? Don't want to block this any further though

Screenshot 2025-10-10 at 8 45 33 AM

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll open a ticket.

@elizabetdev elizabetdev merged commit eaff492 into main Oct 10, 2025
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants